package org.tamal.persistence;

import java.util.Calendar;
import java.util.Date;

import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Transient;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.Proxy;

/**
 * The Person class.
 * @author Tamal Kanti Nath
 */
@Entity
@DynamicUpdate
@Proxy(lazy = false)
@Inheritance(strategy = InheritanceType.JOINED)
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public abstract class Person {

    private Long id;
    private String name;
    private String address;
    private String email;
    private String mobile;
    private Character sex;
    private Date birth;

    /**
     * Returns the id.
     * @return the id
     */
    @Id
    @GeneratedValue
    public Long getId() {
        return id;
    }

    /**
     * Sets the id.
     * @param id the id to set
     */
    public void setId(Long id) {
        this.id = id;
    }

    /**
     * Returns the name.
     * @return the name
     */
    @Column(nullable = false)
    public String getName() {
        return name;
    }

    /**
     * Sets the name.
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * Returns the address.
     * @return the address
     */
    @Column(nullable = false)
    public String getAddress() {
        return address;
    }

    /**
     * Sets the address.
     * @param address the address to set
     */
    public void setAddress(String address) {
        this.address = address;
    }

    /**
     * Returns the email.
     * @return the email
     */
    public String getEmail() {
        return email;
    }

    /**
     * Sets the email.
     * @param email the email to set
     */
    public void setEmail(String email) {
        this.email = email;
    }

    /**
     * Returns the mobile.
     * @return the mobile
     */
    public String getMobile() {
        return mobile;
    }

    /**
     * Sets the mobile.
     * @param mobile the mobile to set
     */
    public void setMobile(String mobile) {
        this.mobile = mobile;
    }

    /**
     * Returns the sex.
     * @return the sex
     */
    @Column(length = 1)
    public Character getSex() {
        return sex;
    }

    /**
     * Sets the sex.
     * @param sex the sex to set
     */
    public void setSex(Character sex) {
        this.sex = sex;
    }

    /**
     * Returns the birth.
     * @return the birth
     */
    @Column(columnDefinition = "date")
    public Date getBirth() {
        return birth;
    }

    /**
     * Sets the birth.
     * @param birth the birth to set
     */
    public void setBirth(Date birth) {
        this.birth = birth;
    }

    /**
     * Calculates the age of the person.
     * @return the age of the person
     */
    @Transient
    public final int getAge() {
        Calendar now = Calendar.getInstance();
        Calendar dob = Calendar.getInstance();
        dob.setTime(birth);
        int age = now.get(Calendar.YEAR) - dob.get(Calendar.YEAR);
        if (now.get(Calendar.DAY_OF_YEAR) < dob.get(Calendar.DAY_OF_YEAR)) {
            --age;
        }
        return age;
    }

    /**
     * Indicates whether the specified object equals to this object.<br/>
     * For Person entity, the name, birth date and sex are considered as
     * business key as they are less likely to change and can be used to
     * identify a record uniquely. Two persons are considered equal if their
     * name, birth date and sex are equal.
     *
     * @param obj the reference object with which to compare
     * @return if this Person is the same as the obj argument; false otherwise
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof Person) {
            Person p = (Person) obj;
            if (name.equals(p.name) && birth.equals(p.birth)
                    && sex.equals(p.sex)) {
                return true;
            }
        }
        return false;
    }

    /**
     * The hash code is generated from name, birth date and sex.
     * @return hash code value for person
     */
    @Override
    public int hashCode() {
        return name.hashCode() ^ birth.hashCode() ^ sex.hashCode();
    }

    @Override
    public String toString() {
        return "Id: " + id + " Name: " + name + " Sex: " + sex + " born: "
                + birth;
    }
}
